﻿using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Reflection;
using BGShell.Components;
using BGShell.Drawing;

namespace BGShell.PowerShell.DataTips
{
    internal class PSObjectRawDataTip : TypeModelDataTip
    {
        public PSObjectRawDataTip(String name, Object obj, Boolean objIsExceptionCaught, Type type)
            : base(name, GetBaseObject(obj), objIsExceptionCaught, type)
        {
        }

        public PSObjectRawDataTip(String name, Object obj)
            : this(name, obj, false, (obj != null) ? obj.GetType() : null)
        {
        }

        protected override IEnumerable<DataTipItem> AppendSubItems()
        {
            var staticMembers = new StaticMembersDataTip(ValueType);

            if (staticMembers.Subitems.Count > 0)
            {
                return new DataTipItem[] { staticMembers };
            }

            return NoItems;
        }

        protected override IEnumerable<DataTipItem> MemberSubItems()
        {
            if (ValueType != null)
            {
                var bf = GetBindingFlags();

                if ((Value != null) || ((bf & BindingFlags.Static) != 0))
                {
                    foreach (var mi in ValueType.GetMembers(bf))
                    {
                        var item = Create(mi);

                        if (item != null)
                        {
                            yield return item;
                        }
                    }
                }
            }
        }

        protected override TypeModelDataTip CreateArrayItem(String name, Object value, Type elementType)
        {
            return new PSObjectRawDataTip(name, value, false, elementType);
        }

        protected virtual BindingFlags GetBindingFlags()
        {
            return BindingFlags.Public | BindingFlags.Instance;
        }

        protected virtual PSObjectRawDataTip Create(Object target, FieldInfo field)
        {
            return new PSObjectRawDataTip(field.Name, field.GetValue(target), false, field.FieldType)
            {
                Icon = TypeModelIcon.Field,
            };
        }

        protected virtual PSObjectRawDataTip Create(Object target, PropertyInfo property)
        {
            var exceptionCaught = false;
            object value = null;

            try
            {
                value = property.GetValue(target, null);
            }
            catch (TargetInvocationException exc)
            {
                value = exc.GetBaseException();
                exceptionCaught = true;
            }

            return new PSObjectRawDataTip(property.Name, value, exceptionCaught, property.PropertyType)
            {
                Icon = TypeModelIcon.Property,
            };
        }

        private PSObjectRawDataTip Create(MemberInfo mi)
        {
            var field = (mi as FieldInfo);
            var property = (mi as PropertyInfo);

            if (field != null)
            {
                return Create(Value, field);
            }

            if ((property != null) && (property.CanRead))
            {
                var indexer = property.GetIndexParameters();

                if ((indexer == null) || (indexer.Length == 0))
                {
                    return Create(Value, property);
                }
            }

            return null;
        }

        private static Object GetBaseObject(Object obj)
        {
            var psobject = (obj as PSObject);

            if (psobject != null)
            {
                return psobject.BaseObject;
            }

            return obj;
        }
    }
}